//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software 
// is granted provided this copyright notice appears in all copies. 
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------
//
// class platform_support
//
//----------------------------------------------------------------------------

#include "platform/agg_platform_support.h"
#include "util/agg_color_conv_rgb8.h"

#include <sys/time.h>
#include <cstring>

#include <classes/requester.h>
#include <classes/window.h>
#include <datatypes/pictureclass.h>
#include <proto/exec.h>
#include <proto/datatypes.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/keymap.h>
#include <proto/Picasso96API.h>
#include <proto/utility.h>

Library* DataTypesBase = 0;
Library* GraphicsBase = 0;
Library* IntuitionBase = 0;
Library* KeymapBase = 0;
Library* P96Base = 0;

DataTypesIFace* IDataTypes = 0;
GraphicsIFace* IGraphics = 0;
IntuitionIFace* IIntuition = 0;
KeymapIFace* IKeymap = 0;
P96IFace* IP96 = 0;

Class* RequesterClass = 0;
Class* WindowClass = 0;


namespace agg
{
  void handle_idcmp(Hook* hook, APTR win, IntuiMessage* msg);

  //------------------------------------------------------------------------
  class platform_specific
  {
  public:
    platform_specific(platform_support& support, pix_format_e format,
      bool flip_y);
    ~platform_specific();
    bool handle_input();
    bool load_img(const char* file, unsigned idx, rendering_buffer* rbuf);
    bool create_img(unsigned idx, rendering_buffer* rbuf, unsigned width,
      unsigned height);
    bool make_bitmap();
  public:
    platform_support& m_support;
    RGBFTYPE m_ftype;
    pix_format_e m_format;
    unsigned m_bpp;
    BitMap* m_bitmap;
    bool m_flip_y;
    uint16 m_width;
    uint16 m_height;
    APTR m_window_obj;
    Window* m_window;
    Hook* m_idcmp_hook;
    unsigned m_input_flags;
    bool m_dragging;
    real m_start_time;
    uint16 m_last_key;
    BitMap* m_img_bitmaps[platform_support::max_images];
  };

  //------------------------------------------------------------------------
  platform_specific::platform_specific(platform_support& support,
    pix_format_e format, bool flip_y) :
    m_support(support),
    m_ftype(RGBFB_NONE),
    m_format(format),
    m_bpp(0),
    m_bitmap(0),
    m_flip_y(flip_y),
    m_width(0),
    m_height(0),
    m_window_obj(0),
    m_window(0),
    m_idcmp_hook(0),
    m_input_flags(0),
    m_dragging(false),
    m_start_time(0.0f),
    m_last_key(0)
  {
    switch ( format )
    {
    case pix_format_gray8:
      // Not supported.
      break;
    case pix_format_rgb555:
      m_ftype = RGBFB_R5G5B5;
      m_bpp = 15;
      break;
    case pix_format_rgb565:
      m_ftype = RGBFB_R5G6B5;
      m_bpp = 16;
      break;
    case pix_format_rgb24:
      m_ftype = RGBFB_R8G8B8;
      m_bpp = 24;
      break;
    case pix_format_bgr24:
      m_ftype = RGBFB_B8G8R8;
      m_bpp = 24;
      break;
    case pix_format_bgra32:
      m_ftype = RGBFB_B8G8R8A8;
      m_bpp = 32;
      break;
    case pix_format_abgr32:
      m_ftype = RGBFB_A8B8G8R8;
      m_bpp = 32;
      break;
    case pix_format_argb32:
      m_ftype = RGBFB_A8R8G8B8;
      m_bpp = 32;
      break;
        case pix_format_rgba32:
      m_ftype = RGBFB_R8G8B8A8;
      m_bpp = 32;
      break;
    }

    for ( unsigned i = 0; i < platform_support::max_images; ++i )
    {
      m_img_bitmaps[i] = 0;
    }
  }

  //------------------------------------------------------------------------
  platform_specific::~platform_specific()
  {
    IIntuition->DisposeObject(m_window_obj);

    IP96->p96FreeBitMap(m_bitmap);

    for ( unsigned i = 0; i < platform_support::max_images; ++i )
    {
      IP96->p96FreeBitMap(m_img_bitmaps[i]);
    }

    if ( m_idcmp_hook != 0 )
    {
      IExec->FreeSysObject(ASOT_HOOK, m_idcmp_hook);
    }
  }

  //------------------------------------------------------------------------
  bool platform_specific::handle_input()
  {
    int16 code = 0;
    uint32 result = 0;
    Object* obj = reinterpret_cast<Object*>(m_window_obj);

    while ( (result = IIntuition->IDoMethod(obj, WM_HANDLEINPUT,
        &code)) != WMHI_LASTMSG )
    {
      switch ( result & WMHI_CLASSMASK )
      {
      case WMHI_CLOSEWINDOW:
        return true;
        break;
      case WMHI_INTUITICK:
        if ( !m_support.wait_mode() )
        {
          m_support.on_idle();
        }
        break;
      case WMHI_NEWSIZE:
        if ( make_bitmap() )
        {
          m_support.trans_affine_resizing(m_width, m_height);
          m_support.on_resize(m_width, m_height);
          m_support.force_redraw();
        }
        break;
      }
    }

    return false;
  }   

  //------------------------------------------------------------------------
  bool platform_specific::load_img(const char* file, unsigned idx,
    rendering_buffer* rbuf)
  {
    if ( m_img_bitmaps[idx] != 0 )
    {
      IP96->p96FreeBitMap(m_img_bitmaps[idx]);
      m_img_bitmaps[idx] = 0;
    }

    bool result = false;

    Object* picture = IDataTypes->NewDTObject(const_cast<STRPTR>(file),
      DTA_GroupID, GID_PICTURE,
      PDTA_DestMode, PMODE_V43,
      PDTA_Remap, FALSE,
      TAG_END);
    if ( picture != 0 )
    {
      gpLayout layout;
      layout.MethodID = DTM_PROCLAYOUT;
      layout.gpl_GInfo = 0;
      layout.gpl_Initial = 1;
      ULONG loaded = IDataTypes->DoDTMethodA(picture, 0, 0,
        reinterpret_cast<Msg>(&layout));
      if ( loaded != 0 )
      {
        BitMap* src_bitmap = 0;
        IDataTypes->GetDTAttrs(picture,
          PDTA_ClassBitMap, &src_bitmap,
          TAG_END);

        bool supported = false;

        RGBFTYPE ftype = static_cast<RGBFTYPE>(IP96->p96GetBitMapAttr(
          src_bitmap, P96BMA_RGBFORMAT));

        switch ( ftype )
        {
        case RGBFB_R8G8B8:
          supported = true;
          break;
        default:
          m_support.message("File uses unsupported graphics mode.");
          break;
        }

        if ( supported )  {
          uint16 width = IP96->p96GetBitMapAttr(src_bitmap,
            P96BMA_WIDTH);
          uint16 height = IP96->p96GetBitMapAttr(src_bitmap,
            P96BMA_HEIGHT);

          m_img_bitmaps[idx] = IP96->p96AllocBitMap(width, height,
            m_bpp, BMF_USERPRIVATE, 0, m_ftype);
          if ( m_img_bitmaps[idx] != 0 )
          {
            int8u* buf = reinterpret_cast<int8u*>(
              IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
              P96BMA_MEMORY));
            int bpr = IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
              P96BMA_BYTESPERROW);
            int stride = (m_flip_y) ? -bpr : bpr;
            rbuf->attach(buf, width, height, stride);

            // P96 sets the alpha to zero so it can't be used to
            // color convert true color modes.
            if ( m_bpp == 32 )
            {
              RenderInfo ri;
              int32 lock = IP96->p96LockBitMap(src_bitmap,
                reinterpret_cast<uint8*>(&ri),
                sizeof(RenderInfo));

              rendering_buffer rbuf_src;
              rbuf_src.attach(
                reinterpret_cast<int8u*>(ri.Memory),
                width, height, (m_flip_y) ?
                  -ri.BytesPerRow : ri.BytesPerRow);

              switch ( m_format )
              {
              case pix_format_bgra32:
                color_conv(rbuf, &rbuf_src,
                  color_conv_rgb24_to_bgra32());
                break;
              case pix_format_abgr32:
                color_conv(rbuf, &rbuf_src,
                  color_conv_rgb24_to_abgr32());
                break;
              case pix_format_argb32:
                color_conv(rbuf, &rbuf_src,
                  color_conv_rgb24_to_argb32());
                break;
              case pix_format_rgba32:
                color_conv(rbuf, &rbuf_src,
                  color_conv_rgb24_to_rgba32());
                break;
              }

              IP96->p96UnlockBitMap(src_bitmap, lock);
            }
            else
            {
              IGraphics->BltBitMap(src_bitmap, 0, 0,
                m_img_bitmaps[idx], 0, 0, width, height,
                ABC|ABNC, 0xFF, 0);
            }

            result = true;
          }
        }
      }
    }

    IGraphics->WaitBlit();
    IDataTypes->DisposeDTObject(picture);

    return result;
  }

  //------------------------------------------------------------------------
  bool platform_specific::create_img(unsigned idx, rendering_buffer* rbuf,
    unsigned width, unsigned height)
  {
    if ( m_img_bitmaps[idx] != 0 )
    {
      IP96->p96FreeBitMap(m_img_bitmaps[idx]);
      m_img_bitmaps[idx] = 0;
    }

    m_img_bitmaps[idx] = IP96->p96AllocBitMap(width, height,
      m_bpp, BMF_USERPRIVATE, m_bitmap, m_ftype);
    if ( m_img_bitmaps[idx] != 0 )
    {
      int8u* buf = reinterpret_cast<int8u*>(
        IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
        P96BMA_MEMORY));
      int bpr = IP96->p96GetBitMapAttr(m_img_bitmaps[idx],
        P96BMA_BYTESPERROW);
      int stride = (m_flip_y) ? -bpr : bpr;

      rbuf->attach(buf, width, height, stride);

      return true;
    }

    return false;
  }

  //------------------------------------------------------------------------
  bool platform_specific::make_bitmap()
  {
    uint32 width = 0;
    uint32 height = 0;
    IIntuition->GetWindowAttrs(m_window,
      WA_InnerWidth, &width,
      WA_InnerHeight, &height,
      TAG_END);

    BitMap* bm = IP96->p96AllocBitMap(width, height, m_bpp,
      BMF_USERPRIVATE|BMF_CLEAR, 0, m_ftype);
    if ( bm == 0 )
    {
      return false;
    }

    int8u* buf = reinterpret_cast<int8u*>(
      IP96->p96GetBitMapAttr(bm, P96BMA_MEMORY));
    int bpr = IP96->p96GetBitMapAttr(bm, P96BMA_BYTESPERROW);
    int stride = (m_flip_y) ? -bpr : bpr;

    m_support.rbuf_window().attach(buf, width, height, stride);

    if ( m_bitmap != 0 )
    {
      IP96->p96FreeBitMap(m_bitmap);
      m_bitmap = 0;
    }

    m_bitmap = bm;
    m_width = width;
    m_height = height;

    return true;
  }

  //------------------------------------------------------------------------
  platform_support::platform_support(pix_format_e format, bool flip_y) :
    m_specific(new platform_specific(*this, format, flip_y)),
    m_format(format),
    m_bpp(m_specific->m_bpp),
    m_window_flags(0),
    m_wait_mode(true),
    m_flip_y(flip_y),
    m_initial_width(10),
    m_initial_height(10)
  {
    std::strncpy(m_caption, "Anti-Grain Geometry", 256);
  }

  //------------------------------------------------------------------------
  platform_support::~platform_support()
  {
    delete m_specific;
  }

  //------------------------------------------------------------------------
  void platform_support::caption(const char* cap)
  {
    std::strncpy(m_caption, cap, 256);
    if ( m_specific->m_window != 0 )
    {
      const char* ignore = reinterpret_cast<const char*>(-1);
      IIntuition->SetWindowAttr(m_specific->m_window,
        WA_Title, m_caption, sizeof(char*));
    }
  }

  //------------------------------------------------------------------------
  void platform_support::start_timer()
  {
    timeval tv;
    gettimeofday(&tv, 0);
    m_specific->m_start_time = tv.tv_secs + tv.tv_micro/1e6;
  }

  //------------------------------------------------------------------------
  real platform_support::elapsed_time() const
  {
    timeval tv;
    gettimeofday(&tv, 0);
    real end_time = tv.tv_secs + tv.tv_micro/1e6;

    real elasped_seconds = end_time - m_specific->m_start_time;
    real elasped_millis = elasped_seconds*1e3;

    return elasped_millis;
  }

  //------------------------------------------------------------------------
  void* platform_support::raw_display_handler()
  {
    return 0; // Not available.
  }

  //------------------------------------------------------------------------
  void platform_support::message(const char* msg)
  {
    APTR req = IIntuition->NewObject(RequesterClass, 0,
      REQ_TitleText, "Anti-Grain Geometry",
      REQ_Image, REQIMAGE_INFO,
      REQ_BodyText, msg,
      REQ_GadgetText, "_Ok",
      TAG_END);
    if ( req == 0 )
    {
      IDOS->Printf("Message: %s\n", msg);
      return;
    }

    orRequest reqmsg;
    reqmsg.MethodID = RM_OPENREQ;
    reqmsg.or_Attrs = 0;
    reqmsg.or_Window = m_specific->m_window;
    reqmsg.or_Screen = 0;
    
    IIntuition->IDoMethodA(reinterpret_cast<Object*>(req),
      reinterpret_cast<Msg>(&reqmsg));
    IIntuition->DisposeObject(req);
  }

  //------------------------------------------------------------------------
  bool platform_support::init(unsigned width, unsigned height,
    unsigned flags)
  {
    if( m_specific->m_ftype == RGBFB_NONE )
    {
      message("Unsupported mode requested.");
      return false;
    }

    m_window_flags = flags;

    m_specific->m_idcmp_hook = reinterpret_cast<Hook*>(
      IExec->AllocSysObjectTags(ASOT_HOOK,
        ASOHOOK_Entry, handle_idcmp,
        ASOHOOK_Data, this,
        TAG_END));
    if ( m_specific->m_idcmp_hook == 0 )
    {
      return false;
    }

    m_specific->m_window_obj = IIntuition->NewObject(WindowClass, 0,
        WA_Title, m_caption,
        WA_AutoAdjustDClip, TRUE,
        WA_InnerWidth, width,
        WA_InnerHeight, height,
        WA_Activate, TRUE,
        WA_SmartRefresh, TRUE,
        WA_NoCareRefresh, TRUE,
        WA_CloseGadget, TRUE,
        WA_DepthGadget, TRUE,
        WA_SizeGadget, (flags & agg::window_resize) ? TRUE : FALSE,
        WA_DragBar, TRUE,
        WA_AutoAdjust, TRUE,
        WA_ReportMouse, TRUE,
        WA_RMBTrap, TRUE,
        WA_MouseQueue, 1,
        WA_IDCMP,
          IDCMP_NEWSIZE |
          IDCMP_MOUSEBUTTONS |
          IDCMP_MOUSEMOVE |
          IDCMP_RAWKEY |
          IDCMP_INTUITICKS,
        WINDOW_IDCMPHook, m_specific->m_idcmp_hook,
        WINDOW_IDCMPHookBits,
          IDCMP_MOUSEBUTTONS |
          IDCMP_MOUSEMOVE |
          IDCMP_RAWKEY,
        TAG_END);
    if ( m_specific->m_window_obj == 0 )
    {
      return false;
    }

    Object* obj = reinterpret_cast<Object*>(m_specific->m_window_obj);
    m_specific->m_window =
      reinterpret_cast<Window*>(IIntuition->IDoMethod(obj, WM_OPEN));
    if ( m_specific->m_window == 0 )
    {
      return false;
    }

    RGBFTYPE ftype = static_cast<RGBFTYPE>(IP96->p96GetBitMapAttr(
      m_specific->m_window->RPort->BitMap, P96BMA_RGBFORMAT));

    switch ( ftype )
    {
    case RGBFB_A8R8G8B8:
    case RGBFB_B8G8R8A8:
    case RGBFB_R5G6B5PC:
      break;
    default:
      message("Unsupported screen mode.\n");
      return false;
    }

    if ( !m_specific->make_bitmap() )
    {
      return false;
    }

    m_initial_width = width;
    m_initial_height = height;

    on_init();
    on_resize(width, height);
    force_redraw();

    return true;
  }

  //------------------------------------------------------------------------
  int platform_support::run()
  {
    uint32 window_mask = 0;
    IIntuition->GetAttr(WINDOW_SigMask, m_specific->m_window_obj,
      &window_mask);
    uint32 wait_mask = window_mask | SIGBREAKF_CTRL_C;

    bool done = false;

    while ( !done )
    {
      uint32 sig_mask = IExec->Wait(wait_mask);
      if ( sig_mask & SIGBREAKF_CTRL_C )
      {
        done = true;
      }
      else
      {
        done = m_specific->handle_input();
      }
    }

    return 0;
  }

  //------------------------------------------------------------------------
  const char* platform_support::img_ext() const
  {
    return ".bmp";
  }

  //------------------------------------------------------------------------
  const char* platform_support::full_file_name(const char* file_name)
  {
    return file_name;
  }

  //------------------------------------------------------------------------
  bool platform_support::load_img(unsigned idx, const char* file)
  {
    if ( idx < max_images )
    {
      static char fn[1024];
      std::strncpy(fn, file, 1024);
      int len = std::strlen(fn);
      if ( len < 4 || std::strcmp(fn + len - 4, ".bmp") != 0 )
      {
        std::strncat(fn, ".bmp", 1024);
      }

      return m_specific->load_img(fn, idx, &m_rbuf_img[idx]);
    }

    return false;
  }

  //------------------------------------------------------------------------
  bool platform_support::save_img(unsigned idx, const char* file)
  {
    message("Not supported");
    return false;
  }

  //------------------------------------------------------------------------
  bool platform_support::create_img(unsigned idx, unsigned width,
    unsigned height)
  {
    if ( idx < max_images )
    {
      if ( width == 0 )
      {
        width = m_specific->m_width;
      }

      if ( height == 0 )
      {
        height = m_specific->m_height;
      }

      return m_specific->create_img(idx, &m_rbuf_img[idx], width,
        height);
    }

    return false;
  }

  //------------------------------------------------------------------------
  void platform_support::force_redraw()
  {
    on_draw();
    update_window();
  }

  //------------------------------------------------------------------------
  void platform_support::update_window()
  {
    // Note this function does automatic color conversion.
    IGraphics->BltBitMapRastPort(m_specific->m_bitmap, 0, 0,
      m_specific->m_window->RPort, m_specific->m_window->BorderLeft,
      m_specific->m_window->BorderTop, m_specific->m_width,
      m_specific->m_height, ABC|ABNC);
  }

  //------------------------------------------------------------------------
  void platform_support::on_init() {}
  void platform_support::on_resize(int sx, int sy) {}
  void platform_support::on_idle() {}
  void platform_support::on_mouse_move(int x, int y, unsigned flags) {}
  void platform_support::on_mouse_button_down(int x, int y, unsigned flags) {}
  void platform_support::on_mouse_button_up(int x, int y, unsigned flags) {}
  void platform_support::on_key(int x, int y, unsigned key, unsigned flags) {}
  void platform_support::on_ctrl_change() {}
  void platform_support::on_draw() {}
  void platform_support::on_post_draw(void* raw_handler) {}

  //------------------------------------------------------------------------
  void handle_idcmp(Hook* hook, APTR obj, IntuiMessage* msg)
  {
    platform_support* app =
      reinterpret_cast<platform_support*>(hook->h_Data);
    Window* window = app->m_specific->m_window;

    int16 x = msg->MouseX - window->BorderLeft;

    int16 y = 0;
    if ( app->flip_y() )
    {
      y = window->Height - window->BorderBottom - msg->MouseY;
    }
    else
    {
      y = msg->MouseY - window->BorderTop;
    }

    switch ( msg->Class )
    {
    case IDCMP_MOUSEBUTTONS:
      if ( msg->Code & IECODE_UP_PREFIX )
      {
        if ( msg->Code == SELECTUP )
        {
          app->m_specific->m_input_flags = mouse_left;
          app->m_specific->m_dragging = false;
        }
        else if ( msg->Code == MENUUP )
        {
          app->m_specific->m_input_flags = mouse_right;
          app->m_specific->m_dragging = false;
        }
        else
        {
          return;
        }


        if ( app->m_ctrls.on_mouse_button_up(x, y) )
        {
          app->on_ctrl_change();
          app->force_redraw();
        }

        app->on_mouse_button_up(x, y, app->m_specific->m_input_flags);
      }
      else
      {
        if ( msg->Code == SELECTDOWN )
        {
          app->m_specific->m_input_flags = mouse_left;
          app->m_specific->m_dragging = true;
        }
        else if ( msg->Code == MENUDOWN )
        {
          app->m_specific->m_input_flags = mouse_right;
          app->m_specific->m_dragging = true;
        }
        else
        {
          return;
        }

        app->m_ctrls.set_cur(x, y);
        if ( app->m_ctrls.on_mouse_button_down(x, y) )
        {
          app->on_ctrl_change();
          app->force_redraw();
        }
        else
        {
          if ( app->m_ctrls.in_rect(x, y) )
          {
            if ( app->m_ctrls.set_cur(x, y) )
            {
              app->on_ctrl_change();
              app->force_redraw();
            }
          }
          else
          {
            app->on_mouse_button_down(x, y,
              app->m_specific->m_input_flags);
          }
        }
      }
      break;
    case IDCMP_MOUSEMOVE:
      if ( app->m_specific->m_dragging )  {
        if ( app->m_ctrls.on_mouse_move(x, y,
           app->m_specific->m_input_flags & mouse_left) != 0 )
        {
          app->on_ctrl_change();
          app->force_redraw();
        }
        else
        {
          if ( !app->m_ctrls.in_rect(x, y) )
          {
            app->on_mouse_move(x, y,
              app->m_specific->m_input_flags);
          }
        }
      }
      break;
    case IDCMP_RAWKEY:
    {
      static InputEvent ie = { 0 };
      ie.ie_Class = IECLASS_RAWKEY;
      ie.ie_Code = msg->Code;
      ie.ie_Qualifier = msg->Qualifier;

      static const unsigned BUF_SIZE = 16;
      static char key_buf[BUF_SIZE];
      int16 num_chars = IKeymap->MapRawKey(&ie, key_buf, BUF_SIZE, 0);

      uint32 code = 0x00000000;
      switch ( num_chars )
      {
      case 1:
        code = key_buf[0];
        break;
      case 2:
        code = key_buf[0]<<8 | key_buf[1];
        break;
      case 3:
        code = key_buf[0]<<16 | key_buf[1]<<8 | key_buf[2];
        break;
      }

      uint16 key_code = 0;

      if ( num_chars == 1 )
      {
        if ( code >= IECODE_ASCII_FIRST && code <= IECODE_ASCII_LAST )
        {
          key_code = code;
        }
      }

      if ( key_code == 0 )
      {
        switch ( code )
        {
        case 0x00000008: key_code = key_backspace;  break;
        case 0x00000009: key_code = key_tab;    break;
        case 0x0000000D: key_code = key_return;   break;
        case 0x0000001B: key_code = key_escape;   break;
        case 0x0000007F: key_code = key_delete;   break;
        case 0x00009B41:
        case 0x00009B54: key_code = key_up;     break;
        case 0x00009B42:
        case 0x00009B53: key_code = key_down;   break;
        case 0x00009B43:
        case 0x009B2040: key_code = key_right;    break;
        case 0x00009B44:
        case 0x009B2041: key_code = key_left;   break;
        case 0x009B307E: key_code = key_f1;     break;
        case 0x009B317E: key_code = key_f2;     break;
        case 0x009B327E: key_code = key_f3;     break;
        case 0x009B337E: key_code = key_f4;     break;
        case 0x009B347E: key_code = key_f5;     break;
        case 0x009B357E: key_code = key_f6;     break;
        case 0x009B367E: key_code = key_f7;     break;
        case 0x009B377E: key_code = key_f8;     break;
        case 0x009B387E: key_code = key_f9;     break;
        case 0x009B397E: key_code = key_f10;    break;
        case 0x009B3F7E: key_code = key_scrollock;  break;
        }
      }

      if ( ie.ie_Code & IECODE_UP_PREFIX )
      {
        if ( app->m_specific->m_last_key != 0 )
        {
          bool left = (key_code == key_left) ? true : false;
          bool right = (key_code == key_right) ? true : false;
          bool down = (key_code == key_down) ? true : false;
          bool up = (key_code == key_up) ? true : false;

          if ( app->m_ctrls.on_arrow_keys(left, right, down, up) )
          {
            app->on_ctrl_change();
            app->force_redraw();
          }
          else
          {
            app->on_key(x, y, app->m_specific->m_last_key, 0);
          }

          app->m_specific->m_last_key = 0;
        }
      }
      else
      {
        app->m_specific->m_last_key = key_code;
      }
      break;
    }
    default:
      break;
    }
  }
}

//----------------------------------------------------------------------------
int agg_main(int argc, char* argv[]);
bool open_libs();
void close_libs();

//----------------------------------------------------------------------------
bool open_libs()
{
  DataTypesBase = IExec->OpenLibrary("datatypes.library", 51);
  GraphicsBase = IExec->OpenLibrary("graphics.library", 51);
  IntuitionBase = IExec->OpenLibrary("intuition.library", 51);
  KeymapBase = IExec->OpenLibrary("keymap.library", 51);
  P96Base = IExec->OpenLibrary("Picasso96API.library", 2);

  IDataTypes = reinterpret_cast<DataTypesIFace*>(
    IExec->GetInterface(DataTypesBase, "main", 1, 0));
  IGraphics = reinterpret_cast<GraphicsIFace*>(
    IExec->GetInterface(GraphicsBase, "main", 1, 0));
  IIntuition = reinterpret_cast<IntuitionIFace*>(
    IExec->GetInterface(IntuitionBase, "main", 1, 0));
  IKeymap = reinterpret_cast<KeymapIFace*>(
    IExec->GetInterface(KeymapBase, "main", 1, 0));
  IP96 = reinterpret_cast<P96IFace*>(
    IExec->GetInterface(P96Base, "main", 1, 0));

  if ( IDataTypes == 0 ||
     IGraphics == 0 ||
     IIntuition == 0 ||
     IKeymap == 0 ||
     IP96 == 0 )
  {
    close_libs();
    return false;
  }
  else
  {
    return true;
  }
}

//----------------------------------------------------------------------------
void close_libs()
{
  IExec->DropInterface(reinterpret_cast<Interface*>(IP96));
  IExec->DropInterface(reinterpret_cast<Interface*>(IKeymap));
  IExec->DropInterface(reinterpret_cast<Interface*>(IIntuition));
  IExec->DropInterface(reinterpret_cast<Interface*>(IGraphics));
  IExec->DropInterface(reinterpret_cast<Interface*>(IDataTypes));

  IExec->CloseLibrary(P96Base);
  IExec->CloseLibrary(KeymapBase);
  IExec->CloseLibrary(IntuitionBase);
  IExec->CloseLibrary(GraphicsBase);
  IExec->CloseLibrary(DataTypesBase);
}

//----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
  if ( !open_libs() )  {
    IDOS->Printf("Can't open libraries.\n");
    return -1;
  }

  ClassLibrary* requester =
    IIntuition->OpenClass("requester.class", 51, &RequesterClass);
  ClassLibrary* window =
    IIntuition->OpenClass("window.class", 51, &WindowClass);
  if ( requester == 0 || window == 0 )
  {
    IDOS->Printf("Can't open classes.\n");
    IIntuition->CloseClass(requester);
    IIntuition->CloseClass(window);
    close_libs();
    return -1;
  }

  int rc = agg_main(argc, argv);

  IIntuition->CloseClass(window);
  IIntuition->CloseClass(requester);
  close_libs();

  return rc;
}
